/**
 * Copyright [2015] Tianfu Ma (matianfu@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * File: buddy.c
 *
 * Created on: Jun 5, 2015
 * Author: Tianfu Ma (matianfu@gmail.com)
 */

#include "../include/errno.h"
#include "../include/data_type.h"
#include "../include/alloc.h"
#include "../include/string.h"
#include "alloc_init.h"
#include "cube_cache.h"

static struct cache_sys * cache_struct;
extern struct page_index *pages;

UINT16 cache_init ()
{
	UINT32 ret;

	cache_struct=(struct cache_sys *) get_firstpagemem_bottom(sizeof(struct cache_sys));
	if(cache_struct ==NULL)
		return 	-1;

	Memset(cache_struct,0,sizeof(*cache_struct));

	cache_struct->pages_num=0;
	cache_struct->index_limit=INDEX_LIMIT;
	cache_struct->total_size=0;
	cache_struct->index_offset=get_firstpagemem_upper(cache_struct->index_limit*sizeof(struct cache_index))
        -page_get_addr(0);

	return (void *)cache_struct - page_get_addr(0);
}

struct cache_index * cache_find_index(int size)
{
	struct cache_index * cache_index;
	void * index_addr=page_get_addr(0)+cache_struct->index_offset;
	int cache_size=((size-1)/4+1)*4;
	int i;
	for(i=1;i<=cache_struct->index_num;i++)
	{
		cache_index=(struct cache_index *)index_addr;
		if(cache_index->cache_size==cache_size)
			return cache_index;		
		index_addr+=sizeof(*cache_index);
	}
	return 0;
}

UINT16 cache_add_page(void * index_addr)
{
	struct cache_index * cache_index = index_addr;
	struct cache_page_index * page_index;
    struct page_index * curr_page_index;
    struct page_index * front_page_index;
    UINT16 front_page;

	if(cache_index->first_page==0)
	{
		cache_index->first_page=get_page();
		if(cache_index->first_page==0)
			return 0;
		cache_index->curr_page=cache_index->first_page;
        pages[cache_index->first_page].type=CACHE_PAGE;
        pages[cache_index->first_page].priv_page = cache_index->first_page;
        pages[cache_index->first_page].next_page = cache_index->first_page;
	}
	else
	{

        front_page=cache_index->curr_page;
        front_page_index = &(pages[front_page]);
		cache_index->curr_page=get_page();
		if(cache_index->curr_page==0)
			return 0;
        curr_page_index = &(pages[cache_index->curr_page]);
        curr_page_index->type=CACHE_PAGE;
        curr_page_index->priv_page = front_page;
        curr_page_index->next_page = front_page_index->next_page;
        front_page_index->next_page = cache_index->curr_page;

        pages[curr_page_index->next_page].priv_page=cache_index->curr_page;
	}

	// page index's offset
    //
    int slot_left=0;

	pages[cache_index->curr_page].state=0;
	page_index=(struct cache_page_index *)(page_get_addr(cache_index->curr_page));
	page_index->cache_size = cache_index->cache_size;


	page_index->empty_slot=PAGE_SIZE/cache_index->cache_size;

    slot_left = PAGE_SIZE-page_index->empty_slot * cache_index->cache_size;

    page_index->index_size=(page_index->empty_slot-1)/8+1;
    page_index->addr_offset=sizeof(page_index)+page_index->index_size;
    while(slot_left < page_index->addr_offset)
    {
        page_index->empty_slot--;
        
        page_index->index_size=(page_index->empty_slot-1)/8+1;
        page_index->addr_offset=sizeof(page_index)+page_index->index_size;
        slot_left+=cache_index->cache_size;
        page_index->addr_offset=sizeof(page_index)+(page_index->empty_slot-1)/8+1;
    }
    
	Memset(page_index->index,0,page_index->index_size);
	
	return cache_index->curr_page;
}

void * cache_add_index(int size)
{
	struct cache_index * cache_index;
	struct cache_page_index * cache_page_index;

	int cache_size=((size-1)/4+1)*4;
	if((cache_size<12) || (cache_size>512))
		return 0;

    if(cache_struct->index_num>=cache_struct->index_limit)
        return 0;
	
    cache_index = (struct cache_index *)(page_get_addr(0) + cache_struct->index_offset + cache_struct->index_num * sizeof(struct cache_index));
	cache_struct->index_num++;

	Memset(cache_index,0,sizeof(*cache_index));
	cache_index->cache_size=cache_size;
	cache_index->page_num=0;
//	cache_index->free_size=PAGE_SIZE;
	return (void *)cache_index;
}

void * Calloc(int size)
{

	UINT32 index_addr;
	UINT32 page_index_addr;
	struct cache_index * cache_index;	
	struct cache_page_index * cache_page_index;	
	UINT32 curr_page;
	UINT16 slot_site;
	void * addr;
	UINT16 page;

	cache_index=cache_find_index(size);
	if(cache_index==NULL)
	{
		cache_index=cache_add_index(size);
		if(cache_index==NULL)
			return 0;
	}

	curr_page=cache_index->curr_page;
    if(curr_page==0)
		curr_page=cache_add_page(cache_index);

	cache_page_index=(struct cache_page_index *)page_get_addr(curr_page);

	if(cache_page_index->empty_slot==0)
	{
		curr_page=cache_add_page(cache_index);
		if(curr_page==0)
			return 0;
	    cache_page_index=(struct cache_page_index *)page_get_addr(curr_page);
	}

	cache_page_index->empty_slot--;
//		cache_index->free_size-=cache_index->cache_size;
	slot_site=Getlowestbit(cache_page_index->index,cache_page_index->index_size,0);			
	bitmap_set(cache_page_index->index,slot_site-1);
	addr=page_get_addr(curr_page)+cache_page_index->addr_offset + (slot_site-1)*cache_index->cache_size;		

	return addr;
}

int CFree(void * addr)
{
    UINT16 cache_page;
    UINT32 offset;
    int slot_site;
    struct cache_page_index * cache_page_index;
    
    cache_page = addr_get_page(addr);
    offset = addr - page_get_addr(cache_page);
	cache_page_index=(struct cache_page_index *)page_get_addr(cache_page);

    slot_site = (offset-cache_page_index->addr_offset)/ cache_page_index->cache_size;
	cache_page_index->empty_slot++;
	bitmap_clear(cache_page_index->index,slot_site);

	//need to add if the whole slots is free,then free this page.
	return 0;		
}
